package com.alipay.util;

import java.io.IOException;
import java.net.BindException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import com.alipay.config.AlipayConfig;
import com.alipay.domain.AlipayRequest;
import com.alipay.sign.MD5;
import com.alipay.util.httpClient.HttpProtocolHandler;
import com.alipay.util.httpClient.HttpRequest;
import com.alipay.util.httpClient.HttpResponse;
import com.alipay.util.httpClient.HttpResultType;

/* *
 *类名：AlipaySubmit
 *功能：支付宝各接口请求提交类
 *详细：构造支付宝各接口表单HTML文本，获取远程HTTP数据
 *版本：3.3
 *日期：2012-08-13
 *说明：
 *以下代码只是为了方便商户测试而提供的样例代码，商户可以根据自己网站的需要，按照技术文档编写,并非一定要使用该代码。
 *该代码仅供学习和研究支付宝接口使用，只是提供一个参考。
 */

public class AlipayUtil {

  public static String generatePayUrl(AlipayRequest reuqest) {
    return generatePayUrl(reuqest.generateParams());
  }

  public static String generatePayUrl(Map<String, String> sParaTemp) {
    // 待请求参数数组
    Map<String, String> sPara = buildRequestPara(sParaTemp);
    List<String> keys = new ArrayList<String>(sPara.keySet());

    StringBuffer sbHtml = new StringBuffer(ALIPAY_GATEWAY_NEW);

    for (int i = 0; i < keys.size(); i++) {
      String name = (String) keys.get(i);
      String value = (String) sPara.get(name);

      sbHtml.append(name).append("=").append(value);
      if (i != keys.size() - 1)
        sbHtml.append("&");

    }

    return sbHtml.toString();
  }

  /**
   * 支付宝提供给商户的服务接入网关URL(新)
   */
  private static final String ALIPAY_GATEWAY_NEW = "https://mapi.alipay.com/gateway.do?";

  /**
   * 生成签名结果
   * 
   * @param sPara
   *          要签名的数组
   * @return 签名结果字符串
   */
  public static String buildRequestMysign(Map<String, String> sPara) {
    String prestr = AlipayCore.createLinkString(sPara); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
    String mysign = "";
    if (AlipayConfig.sign_type.equals("MD5")) {
      mysign = MD5.sign(prestr, AlipayConfig.key, AlipayConfig.input_charset);
    }
    return mysign;
  }

  /**
   * 生成签名结果
   * 
   * @param sPara
   *          要签名的数组
   * @return 签名结果字符串
   */
  public static String buildRequestMysign(Map<String, String> sPara, String key) {
    String prestr = AlipayCore.createLinkString(sPara); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
    String mysign = "";
    if (AlipayConfig.sign_type.equals("MD5")) {
      mysign = MD5.sign(prestr, key, AlipayConfig.input_charset);
    }
    return mysign;
  }

  /**
   * 生成要请求给支付宝的参数数组
   * 
   * @param sParaTemp
   *          请求前的参数数组
   * @return 要请求的参数数组
   */
  private static Map<String, String> buildRequestPara(Map<String, String> sParaTemp) {
    // 除去数组中的空值和签名参数
    Map<String, String> sPara = AlipayCore.paraFilter(sParaTemp);
    // 生成签名结果
    String mysign = buildRequestMysign(sPara);

    // 签名结果与签名方式加入请求提交参数组中
    sPara.put("sign", mysign);
    sPara.put("sign_type", AlipayConfig.sign_type);

    return sPara;
  }

  /**
   * 建立请求，以表单HTML形式构造（默认）
   * 
   * @param sParaTemp
   *          请求参数数组
   * @param strMethod
   *          提交方式。两个值可选：post、get
   * @param strButtonName
   *          确认按钮显示文字
   * @return 提交表单HTML文本
   */
  public static String buildRequest(Map<String, String> sParaTemp, String strMethod, String strButtonName) {
    // 待请求参数数组
    Map<String, String> sPara = buildRequestPara(sParaTemp);
    List<String> keys = new ArrayList<String>(sPara.keySet());

    StringBuffer sbHtml = new StringBuffer();

    sbHtml.append("<form id=\"alipaysubmit\" name=\"alipaysubmit\" action=\"" + ALIPAY_GATEWAY_NEW + "_input_charset=" + AlipayConfig.input_charset
        + "\" method=\"" + strMethod + "\">");

    for (int i = 0; i < keys.size(); i++) {
      String name = (String) keys.get(i);
      String value = (String) sPara.get(name);

      sbHtml.append("<input type=\"hidden\" name=\"" + name + "\" value=\"" + value + "\"/>");
    }

    // submit按钮控件请不要含有name属性
    sbHtml.append("<input type=\"submit\" value=\"" + strButtonName + "\" style=\"display:none;\"></form>");
    sbHtml.append("<script>document.forms['alipaysubmit'].submit();</script>");

    return sbHtml.toString();
  }

  /**
   * 建立请求，以表单HTML形式构造，带文件上传功能
   * 
   * @param sParaTemp
   *          请求参数数组
   * @param strMethod
   *          提交方式。两个值可选：post、get
   * @param strButtonName
   *          确认按钮显示文字
   * @param strParaFileName
   *          文件上传的参数名
   * @return 提交表单HTML文本
   */
  public static String buildRequest(Map<String, String> sParaTemp, String strMethod, String strButtonName, String strParaFileName) {
    // 待请求参数数组
    Map<String, String> sPara = buildRequestPara(sParaTemp);
    List<String> keys = new ArrayList<String>(sPara.keySet());

    StringBuffer sbHtml = new StringBuffer();

    sbHtml.append("<form id=\"alipaysubmit\" name=\"alipaysubmit\"  enctype=\"multipart/form-data\" action=\"" + ALIPAY_GATEWAY_NEW + "_input_charset="
        + AlipayConfig.input_charset + "\" method=\"" + strMethod + "\">");

    for (int i = 0; i < keys.size(); i++) {
      String name = (String) keys.get(i);
      String value = (String) sPara.get(name);

      sbHtml.append("<input type=\"hidden\" name=\"" + name + "\" value=\"" + value + "\"/>");
    }

    sbHtml.append("<input type=\"file\" name=\"" + strParaFileName + "\" />");

    // submit按钮控件请不要含有name属性
    sbHtml.append("<input type=\"submit\" value=\"" + strButtonName + "\" style=\"display:none;\"></form>");

    return sbHtml.toString();
  }

  /**
   * 建立请求，以模拟远程HTTP的POST请求方式构造并获取支付宝的处理结果
   * 如果接口中没有上传文件参数，那么strParaFileName与strFilePath设置为空值 如：buildRequest("",
   * "",sParaTemp)
   * 
   * @param strParaFileName
   *          文件类型的参数名
   * @param strFilePath
   *          文件路径
   * @param sParaTemp
   *          请求参数数组
   * @return 支付宝处理结果
   * @throws Exception
   */
  public static String buildRequest(String strParaFileName, String strFilePath, Map<String, String> sParaTemp) throws Exception {
    // 待请求参数数组
    Map<String, String> sPara = buildRequestPara(sParaTemp);

    HttpProtocolHandler httpProtocolHandler = HttpProtocolHandler.getInstance();

    HttpRequest request = new HttpRequest(HttpResultType.BYTES);
    // 设置编码集
    request.setCharset(AlipayConfig.input_charset);

    request.setParameters(generatNameValuePair(sPara));
    request.setUrl(ALIPAY_GATEWAY_NEW + "_input_charset=" + AlipayConfig.input_charset);

    HttpResponse response = httpProtocolHandler.execute(request, strParaFileName, strFilePath);
    if (response == null) {
      return null;
    }

    String strResult = response.getStringResult();

    return strResult;
  }

  /**
   * MAP类型数组转换成NameValuePair类型
   * 
   * @param properties
   *          MAP类型数组
   * @return NameValuePair类型数组
   */
  private static NameValuePair[] generatNameValuePair(Map<String, String> properties) {
    NameValuePair[] nameValuePair = new NameValuePair[properties.size()];
    int i = 0;
    for (Map.Entry<String, String> entry : properties.entrySet()) {
      nameValuePair[i++] = new NameValuePair(entry.getKey(), entry.getValue());
    }

    return nameValuePair;
  }

  /**
   * 用于防钓鱼，调用接口query_timestamp来获取时间戳的处理函数 注意：远程解析XML出错，与服务器是否支持SSL等配置有关
   * 
   * @return 时间戳字符串
   * @throws IOException
   * @throws DocumentException
   * @throws MalformedURLException
   */
  public static String query_timestamp() throws MalformedURLException, DocumentException, IOException {

    // 构造访问query_timestamp接口的URL串
    String strUrl = ALIPAY_GATEWAY_NEW + "service=query_timestamp&partner=" + AlipayConfig.partner + "&_input_charset" + AlipayConfig.input_charset;
    StringBuffer result = new StringBuffer();

    SAXReader reader = new SAXReader();
    Document doc = reader.read(new URL(strUrl).openStream());

    @SuppressWarnings("unchecked")
    List<Node> nodeList = doc.selectNodes("//alipay/*");

    for (Node node : nodeList) {
      // 截取部分不需要解析的信息
      if (node.getName().equals("is_success") && node.getText().equals("T")) {
        // 判断是否有成功标示
        @SuppressWarnings("unchecked")
        List<Node> nodeList1 = doc.selectNodes("//response/timestamp/*");
        for (Node node1 : nodeList1) {
          result.append(node1.getText());
        }
      }
    }

    return result.toString();
  }

  public static int getRtnCode(String shopURL) throws BindException {
    final HttpClient httpClient = new HttpClient();
    String ss[] = shopURL.split("\\?");
    final PostMethod method = new PostMethod(ss[0]);
    String ps = ss[1];
    if (ps != null) {
      String pss[] = ps.split("&");
      if (pss != null) {
        for (String pas : pss) {
          String params[] = pas.split("=");
          if (params != null && params.length == 2) {
            method.setParameter(params[0], params[1]);
          }
        }
      }
    }

    method.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8");

    try {
      int status = httpClient.executeMethod(method);
      return status;
    } catch (HttpException e) {
      // log.error("error",e);
    } catch (IOException e) {
      // log.error("error",e);
    } finally {
      method.releaseConnection();
    }
    return 0;
  }

  public static void main(String[] args) {

  }
}
